/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.config;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.gateway.config.conditional.ConditionalOnEnabledGlobalFilter;
import org.springframework.cloud.gateway.filter.LoadBalancerServiceInstanceCookieFilter;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.DispatcherHandler;

/**
 * AutoConfiguration for {@link ReactiveLoadBalancerClientFilter}.
 *
 * @author Spencer Gibb
 * @author Olga Maciaszek-Sharma
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ ReactiveLoadBalancer.class, LoadBalancerAutoConfiguration.class, DispatcherHandler.class })
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@EnableConfigurationProperties(GatewayLoadBalancerProperties.class)
public class GatewayReactiveLoadBalancerClientAutoConfiguration {

	/**
	 *
	 * <p>
	 *     在存在负载均衡客户端工厂端时候，
	 *     创建 负载均衡全局过滤器：ReactiveLoadBalancerClientFilter
	 * </p>
	 * 此函数在Spring Cloud Gateway项目中定义了一个Bean，即ReactiveLoadBalancerClientFilter，它是一个过滤器，用于在网关服务中进行负载均衡客户端的配置和处理。该Bean的创建依赖于特定的条件，这些条件通过Spring的@Conditional注解进行指定，确保了只有在满足条件时才会创建这个Bean，从而提高了配置的灵活性和资源的有效利用。
	 * 详细解释如下：
	 *
	 * @Bean：这是Spring框架中用于声明一个Bean的注解，表示该方法会返回一个对象，该对象要注册为Spring应用上下文中的Bean。
	 * @ConditionalOnBean(LoadBalancerClientFactory.class)：这个注解表示只有当LoadBalancerClientFactory类型的Bean存在时，当前方法定义的Bean才会被创建。LoadBalancerClientFactory是用于创建负载均衡客户端的工厂类，它的存在意味着项目中启用了负载均衡功能。
	 * @ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)：这个注解表示只有当ReactiveLoadBalancerClientFilter类型的Bean不存在时，当前方法定义的Bean才会被创建。这避免了在项目中重复创建相同的Bean实例。
	 * @ConditionalOnEnabledGlobalFilter：这是一个自定义的条件注解，通常用于检查是否启用了全局过滤器功能。只有当全局过滤器功能被启用时，当前方法定义的Bean才会被创建。
	 * 方法gatewayLoadBalancerClientFilter接收两个参数：LoadBalancerClientFactory clientFactory和GatewayLoadBalancerProperties properties。clientFactory用于创建负载均衡客户端，properties则包含了负载均衡相关的配置属性，如use404，该属性指示在负载均衡失败时是否返回404错误。
	 * 方法体内，通过调用ReactiveLoadBalancerClientFilter的构造函数，并传入clientFactory和properties作为参数，创建了一个ReactiveLoadBalancerClientFilter实例并返回。这个实例将被注册为Spring应用上下文中的Bean，用于处理网关服务中的负载均衡逻辑。
	 * 在项目的实际业务中，这个函数的作用至关重要。它确保了只有在满足特定条件时，才会创建和配置ReactiveLoadBalancerClientFilter过滤器，从而实现了对网关服务中负载均衡功能的灵活控制。这对于提高网关服务的性能和稳定性具有重要意义，特别是在处理大量请求和复杂路由逻辑时，负载均衡功能能够有效地将请求分发到不同的后端服务，提高系统的吞吐量和响应速度。
	 * 从项目的角度来看，这段代码是网关服务解决方案中的关键部分，它集成了负载均衡功能，使得网关服务能够更高效地处理请求和路由。通过与Spring Cloud Gateway的其他模块和中间件协同工作，这段代码为项目提供了一个强大、灵活且易于配置的网关服务解决方案。
	 * @param clientFactory
	 * @param properties
	 * @return
	 */
	@Bean
	@ConditionalOnBean(LoadBalancerClientFactory.class)
	@ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class)
	@ConditionalOnEnabledGlobalFilter
	public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
			GatewayLoadBalancerProperties properties) {
		return new ReactiveLoadBalancerClientFilter(clientFactory, properties);
	}

	/**
	 * 这段Java代码定义了一个Spring Bean，具体是一个LoadBalancerServiceInstanceCookieFilter实例。让我们逐行解释代码的含义和作用：
	 * @Bean:
	 * 这是一个Spring Framework的注解，用于告诉Spring容器这是一个Bean定义方法。Spring会调用这个方法并将返回的对象注册为Spring上下文中的一个Bean，以便在其他地方进行依赖注入。
	 * @ConditionalOnBean({ ReactiveLoadBalancerClientFilter.class, LoadBalancerClientFactory.class }):
	 *
	 * 这是一个条件注解，它告诉Spring仅当ReactiveLoadBalancerClientFilter和LoadBalancerClientFactory这两个Bean都已经存在于Spring容器中时，才创建这个Bean。
	 * 这通常用于确保只有在某些前置条件满足的情况下（比如依赖的组件已经配置好了），才创建和注册这个Bean。
	 * @ConditionalOnMissingBean:
	 *
	 * 这也是一个条件注解，表示仅当Spring容器中还没有LoadBalancerServiceInstanceCookieFilter类型的Bean时，才创建这个Bean。
	 * 这避免了创建多个相同类型的Bean实例，从而防止冲突。
	 * @ConditionalOnEnabledGlobalFilter:
	 *
	 * 这是一个自定义的条件注解（不是Spring Framework自带的），它的作用通常是根据某些条件（如配置属性）来决定是否启用某个全局过滤器。
	 * 在这个上下文中，它可能表示仅当某个全局过滤功能被启用时，才创建这个LoadBalancerServiceInstanceCookieFilter Bean。
	 * public LoadBalancerServiceInstanceCookieFilter loadBalancerServiceInstanceCookieFilter(LoadBalancerClientFactory loadBalancerClientFactory):
	 *
	 * 这是Bean定义方法的签名。方法名为loadBalancerServiceInstanceCookieFilter，它接收一个LoadBalancerClientFactory类型的参数。
	 * 这个参数将由Spring容器自动注入，前提是LoadBalancerClientFactory已经是一个被管理的Bean。
	 * 方法体:
	 *
	 * Java
	 * return new LoadBalancerServiceInstanceCookieFilter(loadBalancerClientFactory);
	 * 方法体内部创建了一个LoadBalancerServiceInstanceCookieFilter实例，并将loadBalancerClientFactory作为构造参数传递给它。
	 * 然后返回这个新创建的实例，Spring容器会将这个实例注册为一个Bean。
	 * 总结：
	 * 这段代码定义了一个条件性的Spring Bean，即LoadBalancerServiceInstanceCookieFilter。
	 *
	 * 它仅当以下条件全部满足时才会被创建和注册：
	 * ReactiveLoadBalancerClientFilter和LoadBalancerClientFactory这两个Bean都已经存在于Spring容器中。
	 * Spring容器中还没有LoadBalancerServiceInstanceCookieFilter类型的Bean。
	 * 某个全局过滤功能被启用（具体取决于@ConditionalOnEnabledGlobalFilter的实现逻辑）。
	 * 一旦这些条件满足，Spring容器会调用这个方法，创建LoadBalancerServiceInstanceCookieFilter实例，并将其注册为一个Bean，以便在其他地方进行依赖注入和使用。
	 * @param loadBalancerClientFactory
	 * @return
	 */
	@Bean
	@ConditionalOnBean({ ReactiveLoadBalancerClientFilter.class, LoadBalancerClientFactory.class })
	@ConditionalOnMissingBean
	@ConditionalOnEnabledGlobalFilter
	public LoadBalancerServiceInstanceCookieFilter loadBalancerServiceInstanceCookieFilter(
			LoadBalancerClientFactory loadBalancerClientFactory) {
		return new LoadBalancerServiceInstanceCookieFilter(loadBalancerClientFactory);
	}

}
